/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "marshalling.hh"
#include "util.hh"

#include <stdlib.h>
#include <stdio.h>

// DEFINES
#define RET                     "\n"

// TYPES
struct marshall_max_stack { int stack[MARSHALL_MAX_STACK]; };
struct marshall_mid_stack { int stack[MARSHALL_MID_STACK]; };


#ifdef MARSHALL_HIGHT_LEVEL


// CLASS MARSHALLING (High level implementation)
void marshalling::func_call_ret_void(int *esp, void *func_ptr, int n_esp)
{
    if(n_esp<=MARSHALL_MID_STACK)
	((void (*)(marshall_mid_stack))func_ptr)( *(marshall_mid_stack*)esp );
    else
	((void (*)(marshall_max_stack))func_ptr)( *(marshall_max_stack*)esp );
}

int marshalling::func_call_ret_int(int *esp, void *func_ptr, int n_esp)
{
    if(n_esp<=MARSHALL_MID_STACK)
	return ((int (*)(marshall_mid_stack))func_ptr)( *(marshall_mid_stack*)esp );
    else
	return ((int (*)(marshall_max_stack))func_ptr)( *(marshall_max_stack*)esp );
}

double marshalling::func_call_ret_double(int *esp, void *func_ptr, int n_esp)
{
    if(n_esp<=MARSHALL_MID_STACK)
	return ((double (*)(marshall_mid_stack))func_ptr)( *(marshall_mid_stack*)esp );
    else
	return ((double (*)(marshall_max_stack))func_ptr)( *(marshall_max_stack*)esp );
}

long long marshalling::func_call_ret_long_long(int *esp, void *func_ptr, int n_esp)
{
    if(n_esp<=MARSHALL_MID_STACK)
	return ((long long (*)(marshall_mid_stack))func_ptr)( *(marshall_mid_stack*)esp );
    else
	return ((long long (*)(marshall_max_stack))func_ptr)( *(marshall_max_stack*)esp );
}


#else


// CLASS MARSHALLING (Low level [asm] implementation)
void marshalling::func_call_ret_void(int *esp, void *func_ptr)
{
    asm("movl %%esp, -0xc(%%ebp)"RET  // save esp in the stack
	"movl %0, %%esp"RET
	"call *%1"RET
	"movl -0xc(%%ebp), %%esp"     // restore esp in the stack
	:
	:"r"(esp), "r"(func_ptr));
}

int marshalling::func_call_ret_int(int *esp, void *func_ptr)
{
    int ret;
    asm("movl %%esp, -0xc(%%ebp)"RET  // save esp in the stack
	"movl %1, %%esp"RET
	"call *%2"RET
	"movl -0xc(%%ebp), %%esp"RET  // restore esp in the stack
	"movl %%eax, %0"
	:"=r"(ret)
	:"r"(esp), "r"(func_ptr));
    return ret;
}

double marshalling::func_call_ret_double(int *esp, void *func_ptr)
{
    double ret;
    asm("movl %%esp, -0xc(%%ebp)"RET  // save esp in the stack
	"movl %1, %%esp"RET
	"call *%2"RET
	"movl -0xc(%%ebp), %%esp"     // restore esp in the stack
	:"=t"(ret)
	:"r"(esp), "r"(func_ptr));
    return ret;
}

long long marshalling::func_call_ret_long_long(int *esp, void *func_ptr)
{
    unsigned ret1, ret2;
    asm("movl %%esp, -0xc(%%ebp)"RET  // save esp in the stack
	"movl %2, %%esp"RET
	"call *%3"RET
	"movl -0xc(%%ebp), %%esp"RET  // restore esp in the stack
	"movl %%eax, %0"RET
	"movl %%edx, %1"
	:"=r"(ret1), "=r"(ret2)
	:"r"(esp), "r"(func_ptr));
    return (unsigned long long)ret2<<32 | (unsigned long long)ret1;
}


#endif
